'use strict';

class middleware {

  constructor (options = {}) {

    this.globalKey = `_global_${parseInt(Math.random() * 89999 + 10000)}`

    this.chain = {}

    this.chain[ this.globalKey ] = [
      async function (ctx) {
        if (ctx && ctx.exec && typeof ctx.exec === 'function') {
          return await ctx.exec(ctx)
        }
      }
    ]

    this.stack = []

    this.debug = false
    this.cache = false

    if (typeof options === 'object') {
      for (let k in options) {
        switch(k) {
          case 'debug':
            this.debug = options.debug
            break

          case 'cache':
            this.cache = options.cache
            break

          default:;
        }
      }
      
    }
    
  }


  /**
   * 初始化分组，会把全局添加过的中间件存到此处。
   * @param {string} group 
   */
  initGroup (group) {
    //this.init(group);
    let gchain = this.chain[this.globalKey]

    this.chain[group] = []

    for (let i = 0; i < gchain.length; i++) {
      this.chain[group].push(gchain[i])
    }
  }

  /**
   * 
   * @param {function} midcall 
   * @param {null|function} filter 
   * @param {string|null} group 
   */
  add (midcall, filter = null, group = null) {
    if (typeof midcall !== 'function' || midcall.constructor.name !== 'AsyncFunction') {
      throw new Error(`middleware must be a async function`);
    }

    if (typeof filter === 'string') {
      group = filter;
      filter = null;
    }

    if (typeof group === 'string' && group.trim().length > 0) {
      group = group.trim();
    } else {
      group = null;
    }

    let self = this;
    let realmid = function (grp) {
      let last = self.chain[grp].length - 1;
      let nextcall = self.chain[grp][last];

      if (filter !== null && typeof filter === 'function') {
        return async (ctx) => {
          if ( false === await filter(ctx) ) {
            return await nextcall(ctx);
          }
          return await midcall(ctx, nextcall.bind(null, ctx));
        };
      }

      return async (ctx) => {
        return await midcall(ctx, nextcall.bind(null, ctx));
      };

    };

    if (group) {
      if (this.chain[group] === undefined) {
        this.initGroup(group);
      }
      this.chain[group].push(realmid(group));
    } else {
      for (let k in this.chain) {
        this.chain[k].push(realmid(k));
      }
    }
    
  }

  _loadstack () {
    let m;
    while((m = this.stack.pop()) !== undefined) {
      this.add(m.midcall, m.filter, m.group);
    }
  }

  cleanstack () {
    this.stack = [];
  }

  use (midcall, filter = null, group = null) {
    if (typeof filter === 'string') {
      group = filter;
      filter = null;
    }

    this.stack.push({
      midcall: midcall,
      filter: filter,
      group: group
    });
    
    if (this.cache === false) {
      this._loadstack();
    }
  }

  async run (ctx, group='') {
    if (!group || this.chain[group] === undefined) {
      group = this.globalKey;
    }

    let last = this.chain[group].length - 1;

    return await this.chain[group][last](ctx);
  }

}

module.exports = middleware;
